
import htm from 'htm';
import * as PIXI from 'pixi.js'

export let resMgr: ResMgr = null;
export class ResMgr {
    static init() {
        if (resMgr) return;
        console.log('res mgr init ok')
        resMgr = new ResMgr();
    }
    get(name: string, ext?: string) {
        return PIXI.Sprite.from(name);
    }
}

export const widgetMap = new Map();


// 支持的属性
interface IVNODE_PROPS {
    x?: number | string,
    y?: number | string,

    width?: number | string,
    height?: number | string,

    top?: number | string,
    right?: number | string,
    bottom?: number | string,
    left?: number | string,

    textAlign?: string,
    verticalAlign?: string

    is?: string
}
// vnode 结构
export interface IVNODE {
    link?: any,
    parent?: IVNODE,
    children?: IVNODE[] | string[],
    props: IVNODE_PROPS,
    type: string//'div' | 'img' | 'span' | 'component'
}

export const VNODE_TYPE = {
    DIV: 'div',
    IMG: 'img',
    SPAN: 'span',
    COMPONENT: 'component'
}
// 非原生属性 需要特殊处理
const VNODE_PROPS = {
    TOP: 'top',
    LEFT: 'left',
    RIGHT: 'right',
    BOTTOM: 'bottom',
    TEXT_ALIGN: 'textAlign',
    VERTICAL_ALIGN: 'verticalAlign',
    COLOR: 'color',                      // span color 特殊处理
}
const ctors = {
    'div': PIXI.Container,
    'img': PIXI.Sprite,
    'span': PIXI.Text
}
function h(type, props, ...children) {
    props = props || {};
    Object.keys(props).forEach(key => isNumber(props[key]) ? (props[key] = parseInt(props[key])) : null)
    return { type, props, children };
}
export const html = htm.bind(h);



// 组件父类
export class Widget {
    public name?: string;
    public link?: PIXI.DisplayObject;
    public vnode?: any;
    public setName?(name: string) {
        this.name = name;
    }
    public beforeCreate() { };
    public created() { };
    public mounted() { };
    public destroy() {
        console.log('widget will destroy')
        WidgetManager.destroy(this.name)
    }
    public destroyed() { };

    public render(h) { }
}

// 组件管理器
export class WidgetManager {
    static root: IVNODE;

    static init(root: IVNODE) {
        WidgetManager.root = root;
    }

    static registerWidget(name: string, widget: any) {
        widgetMap.set(name, widget);
    }

    static destroy(widgetName: string) {
        const root = WidgetManager.root;
        let widget = widgetMap.get(widgetName);
        let vnode = widget.vnode;
        let index = root.children.indexOf(vnode);
        root.children.splice(index, 1);
    }

    static open(widgetName: string) {
        const root = WidgetManager.root;
        let widget = widgetMap.get(widgetName);
        if (!widget) return console.error('error widget name!!!');
        let vm = new widget();
        let vnode = vm.render(html)
        console.log(vnode);
        callHook(vm, 'beforeCreate');
        let realNode = generateNode(vnode);
        processLayoutCall()
        callHook(vm, 'created');
        root.link.addChild(realNode);
        root.children || (root.children = []).push(vnode);
        callHook(vm, 'mounted');
        return vm;
    }
}

function callHook(widget: Widget, hook: string) {
    const handler = widget[hook];
    handler && handler.call(widget);
}

//-----------------------------------------------------
function getEntityByTag(tag: string, is?: string) {
    let Ctor = ctors[tag], vm;
    if (!Ctor && !is) return console.error('error entityTag!!!');
    if (!Ctor) {
        Ctor = widgetMap.get(is);
        vm = new Ctor().render()
    } else {
        vm = new Ctor()
    }
    return vm;
}

function generateNode(node: IVNODE, root?: PIXI.Container) {
    if (!root) {
        // 设置容器宽高为根节点宽高(也就是屏幕显示区域宽高)
        node.props.width = WidgetManager.root.props.width
        node.props.height = WidgetManager.root.props.height
        root = new PIXI.Container();
        (<any>root).vnode = node;
        node.link = root;
        processProps(root, node, node.props)
    }
    node.children.forEach(vnode => {
        const vm = getEntityByTag(vnode.type, vnode.props.is)
        vm.vnode = vnode;
        vnode.parent = node;
        vnode.link = vm;
        processProps(vm, vnode, vnode.props);
        root.addChild(vm);
        !isSpan(vnode) && vnode.children && generateNode(vnode, vm);
    });

    return root;
}

const onRE = /^@|^on/;
const layoutCallList = [];

function processProps(node: PIXI.DisplayObject, vnode: IVNODE, props) {
    if (!props) return;
    const parent: IVNODE = vnode.parent || WidgetManager.root;
    // 特殊处理的节点
    const handles = {
        [VNODE_TYPE.IMG]: () => {
            // 设置src
            (<PIXI.Sprite>node).texture = resMgr.get(props.src).texture;
        },
        [VNODE_TYPE.SPAN]: () => {
            // 字体默认部分样式
            let defaultStyle = {
                fontFamily: 'Arial',
                fontSize: 18,
                fill: ['#fff'],
                wordWrap: true,
            }
            //颜色
            defaultStyle.fill[0] = props.color ? props.color : defaultStyle.fill[0];
            Object.assign(defaultStyle, props);
            const style = new PIXI.TextStyle(defaultStyle);
            (<PIXI.Text>node).text = vnode.children[0] as string;
            (<PIXI.Text>node).style = style;
        }
    }
    // 前置参数处理
    const handle = handles[vnode.type];
    handle && handle();
    // 父元素宽高
    const height = parent.props.height || parent.link.height;
    const width = parent.props.width || parent.link.width;
    Object.keys(props).forEach(key => {
        let value: any = props[key];
        if (onRE.test(key)) {// 事件处理
            addHandler(node, key, value);
        } else {
            // color 已经处理了
            if (key === VNODE_PROPS.COLOR) return;
            switch (key) {
                case VNODE_PROPS.TOP:
                    var py = parent.props.y || 0;
                    node.y = (py as number) + value;
                    break;
                case VNODE_PROPS.RIGHT:
                    node.x = (width as number) - value;
                    break;
                case VNODE_PROPS.BOTTOM:
                    node.y = (height as number) - value;
                    break;
                case VNODE_PROPS.LEFT:
                    var px = parent.props.x || 0;
                    node.x = (px as number) + value;
                    break;
                default:
                    if (key in node) node[key] = value
                    break;
            }
        }
    });
    // 居中处理 等待元素都创建好后
    if (parent.props[VNODE_PROPS.VERTICAL_ALIGN]) {
        const call = () => {
            const height = parent.link.height;
            if (parent.props[VNODE_PROPS.VERTICAL_ALIGN] === 'center') {
                node.y = (height - (<any>node).height) / 2
            }
        }
        layoutCallList.push(call)
    }
    if (parent.props[VNODE_PROPS.TEXT_ALIGN]) {
        const call = () => {
            const width = parent.link.width;
            if (parent.props[VNODE_PROPS.TEXT_ALIGN] === 'center') {
                node.x = (width - (<PIXI.Text>node).width) / 2
            }
        }
        layoutCallList.push(call)
    }
}

function processLayoutCall() {
    layoutCallList.forEach(fn => fn())
    layoutCallList.length = 0;
}


// 事件处理
function addHandler(node, key, call) {
    node.interactive = true;
    var [key, suffix] = key.split('.');
    const eventName = key.replace(onRE, '');
    if (suffix === 'stop') {
        node.on(eventName, (e) => {
            e.stopped = true;
            e.stopPropagation()
            call(e)
        })
    } else {
        node.on(eventName, call)
    }
}

//util
const isSpan = (vnode) => vnode.type === VNODE_TYPE.SPAN

function isNumber(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
}